package UI;

import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;

import Data.GameData;
import Data.Character;
import Data.GameException;
import Data.GameInfo;
import Data.Player;
import Logic.Game;
import Logic.Settings;
import Logic.Game.GameOverStatus;
import Util.AsyncTask;

/**
 * General class to manage the game and its UI.
 */
public class GameManager implements PlayerNameSelectedCallback, PlayerArmiesSelectedCallback, GameStoppedCallback
{
	private Shell loadingShell;
	
	private Player player1;
	private int player1Id;
	private String player1Name;
	
	private Player player2;
	private int player2Id;
	private String player2Name;
	
	private Game game;
	private GameInfo gameInfo;
	
	/**
	 * Starts a new game or attempts to load a saved one.
	 * 
	 * @param forceNew if true, the method will start a new game even if a savegame is present.
	 */
	public void start(boolean forceNew)
	{
		gameInfo = null;
		game = new Game();
		
		loadingShell = new GameLoadingShell();
		loadingShell.open();
		
		if (!forceNew)
		{
			attemptRetrieveLatestGame();
		}
		else
		{
			openInitialShell();
		}
		
		while (!loadingShell.isDisposed())
		{
			if (!SWTUtil.getDisplay().readAndDispatch())
			{
				SWTUtil.getDisplay().sleep();
			}
		}
		
		loadingShell.dispose();
	}
	
	/**
	 * Attempts to retrieve the state of the last game saved.
	 * On success, it proceeds to try to load the game itself.
	 */
	private void attemptRetrieveLatestGame()
	{
		new AsyncTask()
		{
			private boolean error;
			
			@Override
			protected void onPre()
			{
				error = false;
			}
			
			@Override
			protected void onPerform()
			{
				try
				{
					gameInfo = game.getLatestGame();
				}
				catch (GameException e)
				{
					gameInfo = null;
					error = true;
				}
			}
			
			@Override
			protected void onPost()
			{
				if (error)
				{
					MessageBox mb = new MessageBox(loadingShell);
					
					mb.setText("Error");
					mb.setMessage("Error accessing game-state repository.");
					mb.open();
					
					loadingShell.close();
					
					return;
				}
				
				if (gameInfo != null)
				{
					MessageBox mb = new MessageBox(loadingShell, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
					
					mb.setText("Resume Game?");
					mb.setMessage(String.format("Resume game from %s?", gameInfo.getStartTime()));
					
					if (mb.open() == SWT.YES)
					{
						attemptResumeLatestGame();
					}
					else
					{
						gameInfo = null;
						
						openInitialShell();
					}
				}
				else
				{
					openInitialShell();
				}
			}
		}.start();
	}
	
	/**
	 * Attempts to load the state of the last savegame.
	 * On success, the shell of the game is opened.
	 */
	private void attemptResumeLatestGame()
	{
		new AsyncTask()
		{
			private boolean error;
			
			@Override
			protected void onPre()
			{
				error = false;
			}
			
			@Override
			protected void onPerform()
			{
				try
				{
					game.resumeLatestGame();
				}
				catch (GameException e)
				{
					error = true;
					gameInfo = null;
				}
			}
			
			@Override
			protected void onPost()
			{
				if (error)
				{
					MessageBox mb = new MessageBox(loadingShell);
					
					mb.setText("Error");
					mb.setMessage("Error accessing game-state repository.");
					mb.open();
					
					loadingShell.close();
					
					return;
				}
				
				openInitialShell();
			}
		}.start();
	}
	
	/**
	 * Opens the initial shell of the game.
	 * If a savegame was loaded, the method displays the game board. If not,
	 * the method displays the player name selection shell.
	 */
	private void openInitialShell()
	{
		if ((gameInfo == null) || (game.getGameOverStatus() != GameOverStatus.NotOver))
		{
			if (gameInfo != null)
			{
				MessageBox mb = new MessageBox(loadingShell);
				
				mb.setText("Resume Game");
				mb.setMessage("Previous game has ended, please start a new one.");
				mb.open();
			}
			
			loadingShell.close();
			
			SWTUtil.openShell(new EnterNameShell(this));
		}
		else
		{
			loadingShell.close();
			
			SWTUtil.openShell(new GameShell(this, game));
		}
	}
	
	/**
	 * Called when the names of the players were selected.
	 * The players are either retrieved or inserted to the DB, and the army
	 * building shell is opened.
	 */
	@Override
	public void onPlayerNamesSelected(Shell parent, String player1Name, String player2Name)
	{
		if ((player1Name == null) || (player2Name == null))
			return;
		
		this.player1Name = player1Name;
		this.player2Name = player2Name;
		
		try
		{
			player1Id = GameData.getPlayerIdByName(player1Name);
			player2Id = GameData.getPlayerIdByName(player2Name);
		}
		catch (GameException e)
		{
			MessageBox mb = new MessageBox(parent);
			
			mb.setText("Error");
			mb.setMessage("Unable to create/find players.");
			mb.open();
			
			return;
		}
		finally
		{
			parent.close();
		}
		
		SWTUtil.openShell(new ArmyBuilderShell(this));
	}
	
	/**
	 * Called when the players' armies are selected.
	 * The army is set, the game is started, and the game shell is opened.
	 */
	@Override
	public void onPlayerArmiesSelected(Shell parent, Character[] player1Chars, Character[] player2Chars)
	{
		game = new Game();
		
		player1 = game.getPlayer1();
		player1.setId(player1Id);
		player1.setName(player1Name);
		
		player2 = game.getPlayer2();
		player2.setId(player2Id);
		player2.setName(player2Name);
		
		for (int i = 0; i < Settings.getArmySize(); i++)
		{
			player1.setArmy(player1Chars[i], i);
			player2.setArmy(player2Chars[i], i);
		}
		
		try
		{
			game.startGame();
		}
		catch (GameException e)
		{
			MessageBox mb = new MessageBox(parent);
			
			mb.setText("Error");
			mb.setMessage("Unable to start game");
			mb.open();
			
			return;
		}
		
		parent.close();
		
		SWTUtil.openShell(new GameShell(this, game));
	}
	
	/**
	 * Called when the game is stopped.
	 * The user is asked whether or not they would like to start a new game.
	 * If so, a new game is started; otherwise game manager is done
	 */
	@Override
	public void onGameStopped(Shell parent)
	{
		MessageBox mb = new MessageBox(parent, SWT.ICON_QUESTION | SWT.YES | SWT.NO);
		
		mb.setText("New game");
		mb.setMessage("Would you like to start a new game?");
		
		int result = mb.open();
		
		parent.close();
		
		if (result == SWT.YES)
			start(true);
	}
}
